package com.cirkana.serial;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.util.ArrayList;
import java.util.Date;

public abstract class SerializablePassData {

	private static final byte BOOLEAN_TRUE = 1;
	private static final byte BOOLEAN_FALSE = 0;
	private static final byte BOOLEAN_NULL = -1;

	public abstract void serialize(DataOutputStream out)
			throws SerializationException;

	public abstract void deserialize(DataInputStream in)
			throws SerializationException;

	protected void serializeBoolean(Boolean bool, DataOutputStream out)
			throws SerializationException {
		try {
			if (bool == null)
				out.writeByte(BOOLEAN_NULL);
			else
				out.writeByte(bool ? BOOLEAN_TRUE : BOOLEAN_FALSE);
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected Boolean deserializeBoolean(DataInputStream in)
			throws SerializationException {
		try {
			byte bool = in.readByte();
			if (bool == BOOLEAN_NULL)
				return null;
			else
				return (bool == BOOLEAN_TRUE);
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected void serializeBooleanArray(Boolean[] booleans,
			DataOutputStream out) throws SerializationException {
		try {
			if (booleans == null)
				serializeBoolean(true, out);
			else {
				serializeBoolean(false, out);
				out.writeInt(booleans.length);
				for (Boolean bool : booleans)
					serializeBoolean(bool, out);
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected Boolean[] deserializeBooleanArray(DataInputStream in)
			throws SerializationException {
		try {
			if (deserializeBoolean(in))
				return null;
			else {
				int length = in.readInt();
				Boolean[] a = new Boolean[length];
				for (int iii = 0; iii < length; iii++)
					a[iii] = deserializeBoolean(in);
				return a;
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}

	}

	protected void serializeString(String string, DataOutputStream out)
			throws SerializationException {
		try {
			if (string == null)
				serializeBoolean(true, out);
			else {
				serializeBoolean(false, out);
				out.writeInt(string.length());
				out.writeChars(string);
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}

	}

	protected String deserializeString(DataInputStream in)
			throws SerializationException {
		try {
			if (deserializeBoolean(in))
				return null;
			else {
				int length = in.readInt();
				StringBuffer s = new StringBuffer(length);
				for (int iii = 0; iii < length; iii++)
					s.append(in.readChar());
				return s.toString();
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected void serializeStringArray(String[] strings, DataOutputStream out)
			throws SerializationException {
		try {
			if (strings == null)
				serializeBoolean(true, out);
			else {
				serializeBoolean(false, out);
				out.writeInt(strings.length);
				for (String string : strings)
					serializeString(string, out);
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected String[] deserializeStringArray(DataInputStream in)
			throws SerializationException {
		try {
			if (deserializeBoolean(in))
				return null;
			else {
				int length = in.readInt();
				String[] a = new String[length];
				for (int iii = 0; iii < length; iii++)
					a[iii] = deserializeString(in);
				return a;
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected void serializeInteger(Integer integer, DataOutputStream out)
			throws SerializationException {
		try {
			if (integer == null)
				serializeBoolean(true, out);
			else {
				serializeBoolean(false, out);
				out.writeInt(integer);
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected Integer deserializeInteger(DataInputStream in)
			throws SerializationException {
		try {
			if (deserializeBoolean(in))
				return null;
			else
				return in.readInt();
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected void serializeIntegerArray(Integer[] integers,
			DataOutputStream out) throws SerializationException {
		try {
			if (integers == null)
				serializeBoolean(true, out);
			else {
				serializeBoolean(false, out);
				out.writeInt(integers.length);
				for (Integer integer : integers)
					serializeInteger(integer, out);
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected Integer[] deserializeIntegerArray(DataInputStream in)
			throws SerializationException {
		try {
			if (deserializeBoolean(in))
				return null;
			else {
				int length = in.readInt();
				Integer[] a = new Integer[length];
				for (int iii = 0; iii < length; iii++)
					a[iii] = deserializeInteger(in);
				return a;
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected void serializeLong(Long lon, DataOutputStream out)
			throws SerializationException {
		try {
			if (lon == null)
				serializeBoolean(true, out);
			else {
				serializeBoolean(false, out);
				out.writeLong(lon);
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected Long deserializeLong(DataInputStream in)
			throws SerializationException {
		try {
			if (deserializeBoolean(in))
				return null;
			else
				return in.readLong();
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected void serializeLongArray(Long[] longs, DataOutputStream out)
			throws SerializationException {
		try {
			if (longs == null)
				serializeBoolean(true, out);
			else {
				serializeBoolean(false, out);
				out.writeInt(longs.length);
				for (Long lon : longs)
					serializeLong(lon, out);
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected Long[] deserializeLongArray(DataInputStream in)
			throws SerializationException {
		try {
			if (deserializeBoolean(in))
				return null;
			else {
				int length = in.readInt();
				Long[] a = new Long[length];
				for (int iii = 0; iii < length; iii++)
					a[iii] = deserializeLong(in);
				return a;
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected void serializeDate(Date date, DataOutputStream out)
			throws SerializationException {
		try {
			if (date == null)
				serializeBoolean(true, out);
			else {
				serializeBoolean(false, out);
				out.writeLong(date.getTime());
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected Date deserializeDate(DataInputStream in)
			throws SerializationException {
		try {
			if (deserializeBoolean(in))
				return null;
			else
				return new Date(in.readLong());
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected void serializeDateArray(Date[] dates, DataOutputStream out)
			throws SerializationException {
		try {
			if (dates == null)
				serializeBoolean(true, out);
			else {
				serializeBoolean(false, out);
				out.writeInt(dates.length);
				for (Date date : dates)
					serializeDate(date, out);
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected Date[] deserializeDateArray(DataInputStream in)
			throws SerializationException {
		try {
			if (deserializeBoolean(in))
				return null;
			else {
				int length = in.readInt();
				Date[] a = new Date[length];
				for (int iii = 0; iii < length; iii++)
					a[iii] = deserializeDate(in);
				return a;
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected void serializeDouble(Double doubl, DataOutputStream out)
			throws SerializationException {
		try {
			if (doubl == null)
				serializeBoolean(true, out);
			else {
				serializeBoolean(false, out);
				out.writeDouble(doubl);
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected Double deserializeDouble(DataInputStream in)
			throws SerializationException {
		try {
			if (deserializeBoolean(in))
				return null;
			else
				return in.readDouble();
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected void serializeFloat(Float floa, DataOutputStream out)
			throws SerializationException {
		try {
			if (floa == null)
				serializeBoolean(true, out);
			else {
				serializeBoolean(false, out);
				out.writeFloat(floa);
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected Float deserializeFloat(DataInputStream in)
			throws SerializationException {
		try {
			if (deserializeBoolean(in))
				return null;
			else
				return in.readFloat();
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected void serializeDoubleArray(Double[] doubles, DataOutputStream out)
			throws SerializationException {
		try {
			if (doubles == null)
				serializeBoolean(true, out);
			else {
				serializeBoolean(false, out);
				out.writeInt(doubles.length);
				for (Double doubl : doubles)
					serializeDouble(doubl, out);
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected Double[] deserializeDoubleArray(DataInputStream in)
			throws SerializationException {
		try {
			if (deserializeBoolean(in))
				return null;
			else {
				int length = in.readInt();
				Double[] a = new Double[length];
				for (int iii = 0; iii < length; iii++)
					a[iii] = deserializeDouble(in);
				return a;
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected void serializeFloatArray(Float[] floats, DataOutputStream out)
			throws SerializationException {
		try {
			if (floats == null)
				serializeBoolean(true, out);
			else {
				serializeBoolean(false, out);
				out.writeInt(floats.length);
				for (Float floa : floats)
					serializeFloat(floa, out);
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected Float[] deserializeFloatArray(DataInputStream in)
			throws SerializationException {
		try {
			if (deserializeBoolean(in))
				return null;
			else {
				int length = in.readInt();
				Float[] a = new Float[length];
				for (int iii = 0; iii < length; iii++)
					a[iii] = deserializeFloat(in);
				return a;
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	protected void serializeSerializable(SerializablePassData serializable,
			DataOutputStream out) throws SerializationException {
		try {
			if (serializable == null)
				serializeBoolean(true, out);
			else {
				serializeBoolean(false, out);
				serializable.serialize(out);
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	@SuppressWarnings("unchecked")
	protected SerializablePassData deserializeSerializable(Class type,
			DataInputStream in) throws SerializationException {
		try {
			if (deserializeBoolean(in))
				return null;
			else {
				SerializablePassData serializable = (SerializablePassData) type
						.newInstance();
				serializable.deserialize(in);
				return serializable;
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	@SuppressWarnings("unchecked")
	protected void serializeSerializableList(ArrayList serializables,
			Class type, DataOutputStream out) throws SerializationException {
		try {
			if (serializables == null)
				serializeBoolean(true, out);
			else {
				serializeBoolean(false, out);
				out.writeInt(serializables.size());
				for (Object object : serializables)
					serializeSerializable((SerializablePassData) type
							.cast(object), out);
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}

	@SuppressWarnings("unchecked")
	protected ArrayList deserializeSerializableList(Class type,
			DataInputStream in) throws SerializationException {
		try {
			if (deserializeBoolean(in))
				return null;
			else {
				int length = in.readInt();
				ArrayList<SerializablePassData> list = new ArrayList<SerializablePassData>();
				for (int iii = 0; iii < length; iii++)
					list.add(deserializeSerializable(type, in));
				return list;
			}
		} catch (SerializationException e) {
			throw e;
		} catch (Exception e) {
			throw new SerializationException(e);
		}
	}
}
